#pragma once

#include "base.h"
#include "../impl/ma.h"

namespace mdtx::online::signal
{
    /**
    template <typename T>
    class cusum : public indi_binary2<T>
    {
        using base = indi_binary2<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;

    protected:
        T shi, slo;

    public:
        tuple_out update(tuple_in const &in) override
        {
            auto [x, u0] = in;
            shi = std::max<T>(shi + x - u0, 0);
            slo = std::min<T>(slo + x + u0, 0);
            return tuple_out(shi, slo);
        }
    };
    **/

   template <typename T, bool SIGN>
   class cusum_sq : public indi_unary1<T>
    {
        using base = indi_unary1<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        impl::sum<T> s{};

    public:
        tuple_out update(tuple_in const &in) override
        {
            T x = std::get<0>(in), x2;
            if constexpr (SIGN)
            {
                x2 = x * x * sign(x);
            }
            else
            {
                x2 = x * x;
            }
            s.insert_impl(x2);
            return tuple_out(s.s);
        }
    };

   template <typename T, bool SIGN>
   class rosum_sq: public indi_unary1<T>
    {
        using base = indi_unary1<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::update;

    protected:
        boost::circular_buffer<T> buf;
        impl::sum<T> s{};

    public:
        explicit rosum_sq(ptrdiff_t period)
            : buf(period)
        {
        }

        tuple_out update(tuple_in const &in) override
        {
            T x = std::get<0>(in), x2;
            if constexpr (SIGN)
            {
                x2 = x * x * sign(x);
            }
            else
            {
                x2 = x * x;
            }

            if (buf.full())
            {
                s.roll_impl(x2, buf.front());
            }
            else
            {
                s.insert_impl(x2);
            }
            buf.push_back(x2);

            return tuple_out(s.s);
        }
    };
}
